package org.rambadger.command.model;

import org.axonframework.eventhandling.annotation.EventHandler;
import org.axonframework.eventsourcing.annotation.AbstractAnnotatedEntity;
import org.rambadger.command.event.AssignmentAddedToTrack;
import org.rambadger.command.event.AssignmentRemovedFromTrack;

import javax.validation.constraints.Min;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;

import static org.rambadger.command.event.FileDeleted.AssignmentFileDeleted;
import static org.rambadger.command.event.FileUploaded.AssignmentFileUploaded;


/**
 * Entity type representing an assignment.
 *
 * @see
 * @see
 */
public class AssignmentEntity extends AbstractAnnotatedEntity implements Serializable {
    private static final long serialVersionUID = 8745695118906695166L;

    private final String assignmentId;
    private String assignmentName;
    private String description;

    private int submissionPoints;
    private int reviewPoints;

    private Map<String, String> attachments = new HashMap<>();

    private String associatedTrack;

    /**
     * Instantiates a new Assignment entity.
     *
     * @param assignmentId the assignment id
     * @param assignmentName the assignment name
     * @param description the description
     * @param submissionPoints the maximum number of submission point a reviewer may award a user for this assignment
     * @param reviewPoints how many review points a reviewer receives for completing a review of a submission for this assignment
     */
    public AssignmentEntity(String assignmentId, String assignmentName, String description, int submissionPoints, int reviewPoints) {
        this.assignmentId = assignmentId;
        this.assignmentName = assignmentName;
        this.description = description;
        this.submissionPoints = submissionPoints;
        this.reviewPoints = reviewPoints;

        this.associatedTrack = null;
    }

    @EventHandler void on(AssignmentFileUploaded event) {
        if (event.getAssignmentId().equals(assignmentId)) {
            attachments.put(event.getFilename(), event.getFileId());
        }
    }

    @EventHandler void on(AssignmentFileDeleted event) {
        if (event.getAssignmentId().equals(assignmentId)) {
            attachments.remove(event.getFilename());
        }
    }

    @EventHandler void on(AssignmentAddedToTrack event) {
        if (event.getAssignmentId().equals(assignmentId)) {
            associatedTrack = event.getTrackId();
        }
    }

    @EventHandler void on(AssignmentRemovedFromTrack event) {
        if (event.getAssignmentId().equals(assignmentId)) {
            associatedTrack = null;
        }
    }

    /**
     * Gets the assignment id.
     *
     * @return the assignment id
     */
    public String getAssignmentId() {
        return assignmentId;
    }

    /**
     * Gets the assignment name.
     *
     * @return the assignment name
     */
    public String getAssignmentName() {
        return assignmentName;
    }

    /**
     * Gets the description.
     *
     * @return the description
     */
    public String getDescription() {
        return description;
    }

    /**
     * Gets the maximum number of submission points that may be awarded to a user for completing the assignment.
     *
     * @return the maximum submission points awardable.
     */
    @Min(0)
    public int getSubmissionPoints() {
        return submissionPoints;
    }

    /**
     * Gets the number review points awarded to a reviewer for completing a review of this assignment.
     * <p>
     *     Unlike submission points, the maximum number of review points are always awarded
     * </p>
     *
     * @return the review points to be awarded
     */
    @Min(0)
    public int getReviewPoints() {
        return reviewPoints;
    }


    /**
     * Checks whether an attachment with the filename provided is present on the assignment.
     *
     * @param filename the filename
     * @return the boolean
     */
    public boolean hasAttachment(String filename) {
        return attachments.containsKey(filename);
    }

    /**
     * Gets the map containing the attachments on the assignment.
     * @return the map of attachments.
     */
    public Map<String, String> getAttachments() {
        return attachments;
    }

    /**
     * Determines whether the Assignment is attached to a {@link TrackEntity track}.
     *
     * @return whether the Assignment is attached to a track
     */
    public boolean isAssigned() {
        return associatedTrack != null;
    }

    /**
     * Gets the id of the {@link TrackEntity track} that the assignment is attached to.
     *
     * @return the associated track id
     */
    public String getAssociatedTrack() {
        return associatedTrack;
    }

    /**
     * Determines whether the assignment is either free (not in any track) <em>or</em> is part of the given track.
     * <p>
     *     The opposing case, where the assignment is part of a different track, prevents the assignment from being moved to the specified one.
     * </p>
     * @param trackId the track to check against
     * @return whether the assignment is either in the specified track or has no track
     */
    public boolean isFreeOrInTrack(String trackId) {
        return (!isAssigned()) || (getAssociatedTrack().equals(trackId));
    }
}
